{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0e37ac0a",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31ba04c6",
   "metadata": {},
   "source": [
    "## IP地址"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97ca966c",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a760686",
   "metadata": {},
   "source": [
    "IP是Internet Protocol Address，即\"互联网协议地址\"。用来标识网络中的一个通信实体的地址。\n",
    "\n",
    "\n",
    "通信实体可以是计算机、路由器等。比如互联网的每个服务器都要有自己的IP地址，而每个局域网的计算机要通信也要配置IP地址。\n",
    "\n",
    "路由器是连接两个或多个网络的网络设备。\n",
    "\n",
    "\n",
    "IP 地址就像是我们的家庭住址一样，如果你要写信给一个人，你就要知道他（她）的地址，这样邮递员才能把信送到。\n",
    "\n",
    "\n",
    "计算机发送信息就好比是邮递员，它必须知道唯一的“家庭地址”才能不至于把信送错人家。\n",
    "\n",
    "\n",
    "只不过我们的地址是用文字来表示的，计算机的地址用二进制数字表示。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c42c9bcb",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38d22b49",
   "metadata": {},
   "source": [
    "目前主流使用的IP地址是IPV4，但是随着网络规模的不断扩大，IPV4面临着枯竭的危险，所以推出了IPV6。\n",
    "\n",
    "**IPv4：**\n",
    "\n",
    "- 采用32位地址长度，只有大约43亿个地址，它只有4段数字，每一段最大不超过255。\n",
    "\n",
    "- 随着互联网的发展，IP地址不够用了，在2019年11月25日IPv4位地址分配完毕。\n",
    "\n",
    "\n",
    "**IPv6：**\n",
    "- 采用128位地址长度，几乎可以不受限制地提供地址。\n",
    "- 以字符串表示类似于 2001:0db8:85a3:0042:1000:8a2e:0370:7334"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6aafd0ae",
   "metadata": {},
   "source": [
    "**公有地址:**\n",
    "- 公有地址（Public address）由Inter NIC（Internet NetworkInformation Center互联网信息中心）负责。\n",
    "\n",
    "- 这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问互联网。\n",
    "\n",
    "**私有地址:**\n",
    "\n",
    "私有地址（Private address）属于非注册地址，专门为组织机构内部使用。以下列出留用的内部私有地址\n",
    "- A类 10.0.0.0--10.255.255.255\n",
    "- B类 172.16.0.0--172.31.255.255\n",
    "- C类 192.168.0.0--192.168.255.255\n",
    "\n",
    "**注意事项:**\n",
    "- 127.0.0.1 本机地址\n",
    "- 192.168.0.0--192.168.255.255为私有地址，属于非注册地址，专门为组织机构内部使用"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58f6865f",
   "metadata": {},
   "source": [
    "## 查看IP地址"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b319e31d",
   "metadata": {},
   "source": [
    "**查看IP地址：**\n",
    "\n",
    "windows下，我们可以通过命令 ipconfig 获取网卡信息。（Linux和Mac，是 ifconfig ） \n",
    "\n",
    "**通过 ping 查看网络连接：**\n",
    "1. ping www.baidu.com 查看是否能上公网 \n",
    "1. ping 192.168.1.100 查看是否和该计算机在同一个局域网 \n",
    "1. ping 127.0.0.1 查看本机网卡是否可用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8cda03d0",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-08T03:48:37.469878Z",
     "start_time": "2023-10-08T03:48:37.317044Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384\r\n",
      "\toptions=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>\r\n",
      "\tinet 127.0.0.1 netmask 0xff000000 \r\n",
      "\tinet6 ::1 prefixlen 128 \r\n",
      "\tinet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280\r\n",
      "stf0: flags=0<> mtu 1280\r\n",
      "anpi1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 12:9f:83:26:15:4d \r\n",
      "\tinet6 fe80::109f:83ff:fe26:154d%anpi1 prefixlen 64 scopeid 0x4 \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: none\r\n",
      "\tstatus: inactive\r\n",
      "anpi0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 12:9f:83:26:15:4c \r\n",
      "\tinet6 fe80::109f:83ff:fe26:154c%anpi0 prefixlen 64 scopeid 0x5 \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: none\r\n",
      "\tstatus: inactive\r\n",
      "en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 12:9f:83:26:15:2c \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: none\r\n",
      "\tstatus: inactive\r\n",
      "en4: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 12:9f:83:26:15:2d \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: none\r\n",
      "\tstatus: inactive\r\n",
      "en1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=460<TSO4,TSO6,CHANNEL_IO>\r\n",
      "\tether 36:97:44:b7:2d:c0 \r\n",
      "\tmedia: autoselect <full-duplex>\r\n",
      "\tstatus: inactive\r\n",
      "en2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=460<TSO4,TSO6,CHANNEL_IO>\r\n",
      "\tether 36:97:44:b7:2d:c4 \r\n",
      "\tmedia: autoselect <full-duplex>\r\n",
      "\tstatus: inactive\r\n",
      "ap1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 3e:91:80:f2:db:31 \r\n",
      "\tmedia: autoselect\r\n",
      "en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether 1c:91:80:f2:db:31 \r\n",
      "\tinet6 fe80::495:eaf7:b412:e01a%en0 prefixlen 64 secured scopeid 0xb \r\n",
      "\tinet 10.194.168.254 netmask 0xffffc000 broadcast 10.194.191.255\r\n",
      "\tinet6 2001:da8:204:2104:5834:80c0:f6:5a0 prefixlen 128 dynamic \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: autoselect\r\n",
      "\tstatus: active\r\n",
      "bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=63<RXCSUM,TXCSUM,TSO4,TSO6>\r\n",
      "\tether 36:97:44:b7:2d:c0 \r\n",
      "\tConfiguration:\r\n",
      "\t\tid 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0\r\n",
      "\t\tmaxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200\r\n",
      "\t\troot id 0:0:0:0:0:0 priority 0 ifcost 0 port 0\r\n",
      "\t\tipfilter disabled flags 0x0\r\n",
      "\tmember: en1 flags=3<LEARNING,DISCOVER>\r\n",
      "\t        ifmaxaddr 0 port 8 priority 0 path cost 0\r\n",
      "\tmember: en2 flags=3<LEARNING,DISCOVER>\r\n",
      "\t        ifmaxaddr 0 port 9 priority 0 path cost 0\r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: <unknown type>\r\n",
      "\tstatus: inactive\r\n",
      "awdl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=6463<RXCSUM,TXCSUM,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>\r\n",
      "\tether e6:96:81:f3:0a:4f \r\n",
      "\tinet6 fe80::e496:81ff:fef3:a4f%awdl0 prefixlen 64 scopeid 0xd \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: autoselect\r\n",
      "\tstatus: active\r\n",
      "llw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500\r\n",
      "\toptions=400<CHANNEL_IO>\r\n",
      "\tether e6:96:81:f3:0a:4f \r\n",
      "\tinet6 fe80::e496:81ff:fef3:a4f%llw0 prefixlen 64 scopeid 0xe \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "\tmedia: autoselect\r\n",
      "\tstatus: inactive\r\n",
      "utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380\r\n",
      "\tinet6 fe80::4bcc:c0b3:e05a:c5d8%utun0 prefixlen 64 scopeid 0xf \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000\r\n",
      "\tinet6 fe80::e737:3950:2cd4:a9c6%utun1 prefixlen 64 scopeid 0x10 \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n",
      "utun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1000\r\n",
      "\tinet6 fe80::ce81:b1c:bd2c:69e%utun2 prefixlen 64 scopeid 0x11 \r\n",
      "\tnd6 options=201<PERFORMNUD,DAD>\r\n"
     ]
    }
   ],
   "source": [
    "!ifconfig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4cd77289",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-08T03:53:22.561318Z",
     "start_time": "2023-10-08T03:53:14.903346Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PING www.a.shifen.com (110.242.68.3): 56 data bytes\n",
      "64 bytes from 110.242.68.3: icmp_seq=0 ttl=51 time=28.954 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=1 ttl=51 time=16.640 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=2 ttl=51 time=26.581 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=3 ttl=51 time=15.064 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=4 ttl=51 time=67.410 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=5 ttl=51 time=41.024 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=6 ttl=51 time=18.121 ms\n",
      "64 bytes from 110.242.68.3: icmp_seq=7 ttl=51 time=27.468 ms\n",
      "^C\n",
      "\n",
      "--- www.a.shifen.com ping statistics ---\n",
      "8 packets transmitted, 8 packets received, 0.0% packet loss\n",
      "round-trip min/avg/max/stddev = 15.064/30.158/67.410/16.135 ms\n"
     ]
    }
   ],
   "source": [
    "# 查看是否能上公网\n",
    "!ping www.baidu.com "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d8ccd352",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-08T03:53:52.882107Z",
     "start_time": "2023-10-08T03:53:46.741793Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PING 192.168.1.100 (192.168.1.100): 56 data bytes\n",
      "Request timeout for icmp_seq 0\n",
      "Request timeout for icmp_seq 1\n",
      "Request timeout for icmp_seq 2\n",
      "Request timeout for icmp_seq 3\n",
      "Request timeout for icmp_seq 4\n",
      "^C\n",
      "\n",
      "--- 192.168.1.100 ping statistics ---\n",
      "6 packets transmitted, 0 packets received, 100.0% packet loss\n"
     ]
    }
   ],
   "source": [
    "# 查看是否和该计算机在同一个局域网 \n",
    "!ping 192.168.1.100 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ddbd8da6",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-08T03:54:00.884577Z",
     "start_time": "2023-10-08T03:53:56.561420Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PING 127.0.0.1 (127.0.0.1): 56 data bytes\n",
      "64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.086 ms\n",
      "64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.173 ms\n",
      "64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.141 ms\n",
      "64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.155 ms\n",
      "64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.138 ms\n",
      "^C\n",
      "\n",
      "--- 127.0.0.1 ping statistics ---\n",
      "5 packets transmitted, 5 packets received, 0.0% packet loss\n",
      "round-trip min/avg/max/stddev = 0.086/0.139/0.173/0.029 ms\n"
     ]
    }
   ],
   "source": [
    "# 查看本机网卡是否可用\n",
    "!ping 127.0.0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa7082a7",
   "metadata": {},
   "source": [
    "## 端口port\n",
    "端口号用来识别计算机中进行通信的应用程序。因此，它也被称为程序地址。\n",
    "\n",
    "一台计算机上同时可以运行多个程序。传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序，并准确地进行数据传输"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f9065e0",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/4.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb171638",
   "metadata": {},
   "source": [
    "**端口分配:**\n",
    "\n",
    "通过端口，可以在一个主机上运行多个网络应用程序。 端口的表示是一个16位的二进制整数，对应十进制的0-65535。\n",
    "\n",
    "操作系统中一共提供了0~65535可用端口范围。\n",
    "\n",
    "\n",
    "\n",
    "**按端口号分类：**\n",
    "- 公认端口（Well Known Ports）：\n",
    "    - 从0到1023，它们紧密绑定（binding）于一些服务。\n",
    "    - 通常这些端口的通讯明确表明了某种服务的协议。例如：80端口实际上总是HTTP通讯。\n",
    "\n",
    "\n",
    "- 注册端口（Registered Ports）：\n",
    "    - 从1024到65535。它们松散地绑定于一些服务。\n",
    "    - 也就是说有许多服务绑定于这些端口，这些端口同样用于许多其它目的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d957310f",
   "metadata": {},
   "source": [
    "## 网络通信协议"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cd44dc3",
   "metadata": {},
   "source": [
    "国际标准化组织(ISO，即International Organization forStandardization)定义了网络通信协议的基本框架，被称为OSI（Open System Interconnect，即开放系统互联）模型。\n",
    "\n",
    "OSI七层协议模型如图所示:\n",
    "\n",
    "![](02_网络编程_images/5.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7124aa34",
   "metadata": {},
   "source": [
    "虽然国际标准化组织制定了这样一个网络通信协议的模型，但是实际上互联网通讯使用最多的网络通信协议是TCP/IP网络通信协议。\n",
    "\n",
    "TCP/IP 是一个协议族，也是按照层次划分，共四层：应用层，传输层，互连网络层，网络接口层（物理+数据链路层）\n",
    "\n",
    "ISO模型与TCP/IP模型的对应关系如图所示:\n",
    "\n",
    "![](02_网络编程_images/6.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42abd78d",
   "metadata": {},
   "source": [
    "## TCP和UDP协议"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40d09c02",
   "metadata": {},
   "source": [
    "TCP 用于在传输层有必要实现可靠传输的情况；\n",
    "\n",
    "UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。\n",
    "\n",
    "![](02_网络编程_images/7.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4bdc487",
   "metadata": {},
   "source": [
    "### TCP \n",
    "TCP（Transmission Control Protocol，传输控制协议）\n",
    "\n",
    "TCP方式就类似于拨打电话，使用该种方式进行网络通讯时，需要建立专门的虚拟连接，然后进行可靠的数据传输，\n",
    "\n",
    "如果数据发送失败，则客户端会自动重发该数据。\n",
    "\n",
    "![](02_网络编程_images/8.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb59324a",
   "metadata": {},
   "source": [
    "### UDP\n",
    "UDP（User Data Protocol，用户数据报协议）\n",
    "\n",
    "UDP是一个非连接的协议，传输数据之前源端和终端不建立连接，当它想传送时就简单地去抓取来自应用程序的数据，并尽可能快地把它扔到网络上。 \n",
    "\n",
    "在发送端，UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制； \n",
    "\n",
    "在接收端，UDP把每个消息段放在队列中，应用程序每次从队列中读一个消息段。\n",
    "\n",
    "UDP方式就类似于发送短信，使用这种方式进行网络通讯时，不需要建立专门的虚拟连接，传输也不是很可靠，如果发送失败则客户端无法获得。\n",
    "\n",
    "UDP 因为没有拥塞控制，一直会以恒定的速度发送数据。即使网络条件不好，也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包\n",
    "\n",
    "但是优点也很明显，在某些实时性要求高的场景（比如电话会议）就需要使用 UDP 而不是 TCP\n",
    "\n",
    "\n",
    "![](02_网络编程_images/9.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3374597c",
   "metadata": {},
   "source": [
    "### TCP 和 UDP 区别\n",
    "这两种传输方式都在实际的网络编程中使用，重要的数据一般使用 TCP方式 进行数据传输，而大量的非核心数据则可以通过UDP方式进行传递，在一些程序中甚至结合使用这两种方式进行数据传递。\n",
    "\n",
    "由于TCP需要建立专用的虚拟连接以及确认传输是否正确，所以使用TCP方式的速度稍微慢一些，而且传输时产生的数据量要比 UDP 稍微大一些。\n",
    "\n",
    "![](02_网络编程_images/10.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad8764e4",
   "metadata": {},
   "source": [
    "## TCP建立连接的三次握手"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3236295b",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/11.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bcf69df0",
   "metadata": {},
   "source": [
    "TCP是面向连接的协议，也就是说，在收发数据前，必须和对方建立可靠的连接。 \n",
    "\n",
    "一个TCP连接必须要经过三次“对话”才能建立起来。\n",
    "\n",
    "\n",
    "**其中的过程非常复杂， 这里只简单的描述下这三次对话的简单过程：**\n",
    "1. 主机A向主机B发出连接请求：“我想给你发数据，可以吗？”，这是第一次对话；\n",
    "\n",
    "2. 主机B向主机A发送同意连接和要求同步 （同步就是两台主机一个在发送，一个在接收，协调工作）的数据包 ：“可以，你什么时候发？”，这是第二次对话；\n",
    "\n",
    "3. 主机A再发出一个数据包确认主机B的要求同步：“我现在就发，你接着吧！”， 这是第三次握手。\n",
    "\n",
    "\n",
    "三次“对话”的目的是使数据包的发送和接收同步， 经过三次“对话”之后，主机A才向主机B正式发送数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be511345",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/12.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2aa19c11",
   "metadata": {},
   "source": [
    "1. 客户端发送一个包含 SYN即同步（Synchronize）标志的TCP报文，SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。\n",
    "\n",
    "\n",
    "2. 服务器在收到客户端的SYN报文后，将返回一个SYN+ACK的报文，表示客户端的请求被接受，同时TCP序号被加一，ACK即确认（Acknowledgement） \n",
    "\n",
    "\n",
    "3. 客户端也返回一个确认报文ACK给服务器端，同样TCP序列号被加一，到此一个TCP连接完成。然后才开始通信的第二步：数据处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55041c56",
   "metadata": {},
   "source": [
    "**为什么TCP协议有三次握手，而UDP协议没有？**\n",
    "\n",
    "因为三次握手的目的是在 client端 和 server端 建立可靠的连接。保证双方发送的数据对方都能接受到，这也是TCP协议的被称为可靠的数据传输协议的原因。\n",
    "\n",
    "而UDP就不一样，UDP不提供可靠的传输模式，发送端并不需要得到接收端的状态，因此UDP协议就用不着使用三次握手。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31582070",
   "metadata": {},
   "source": [
    "## TCP断开连接的四次挥手\n",
    "\n",
    "![](02_网络编程_images/13.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd9b8a86",
   "metadata": {},
   "source": [
    "**TCP建立连接要进行3次握手，而断开连接要进行4次：**\n",
    "1. 当主机A完成数据传输后,将控制位FIN置1，提出停止TCP连接的请求 ；\n",
    "\n",
    "\n",
    "2. 主机B收到FIN后对其作出响应，确认这一方向上的TCP连接将关闭，将ACK置1；\n",
    "\n",
    "\n",
    "3. 由B 端再提出反方向的关闭请求,将FIN置1 ；\n",
    "\n",
    "\n",
    "4. 主机A对主机B的请求进行确认，将ACK置1，双方向的关闭结束\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e2c0cc6",
   "metadata": {},
   "source": [
    "## 数据包与处理流程\n",
    "### 数据包\n",
    "通信传输中的数据单位，一般也称“数据包”。在数据包中包括：包、帧、数据包、段、消息。\n",
    "\n",
    "网络中传输的数据包由两部分组成：\n",
    "- 一部分是协议所要用到的首部\n",
    "- 另一部分是上一层传过来的数据。\n",
    "\n",
    "首部的结构由协议的具体规范详细定义。在数据包的首部，明确标明了协议应该如何读取数据。\n",
    "\n",
    "反过来说，看到首部，也就能够了解该协议必要的信息以及所要处理的数据。\n",
    "\n",
    "![](02_网络编程_images/14.png)\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e1a9c11c",
   "metadata": {},
   "source": [
    "### 数据包处理流程"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2a97ce4",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/15.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41cf72d5",
   "metadata": {},
   "source": [
    "## socket编程"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a8409bd",
   "metadata": {},
   "source": [
    "TCP协议 和 UDP协议 是传输层的两种协议。Socket是传输层供给应用层的编程接口，所以Socket编程就分为TCP编程和UDP编程两类\n",
    "\n",
    "![](02_网络编程_images/16.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eabccf73",
   "metadata": {},
   "source": [
    "Socket编程封装了常见的TCP、UDP操作，可以实现非常方便的网络编程\n",
    "\n",
    "![](02_网络编程_images/17.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bf91c4c",
   "metadata": {},
   "source": [
    "### socket() 函数\n",
    "在Python语言标准库中，通过使用socket模块提供的socket对象，可以在计算机网络中建立可以互相通信的服务器与客户端。\n",
    "\n",
    "在服务器端需要建立一个socket对象，并等待客户端的连接。\n",
    "\n",
    "客户端使用socket对象与服务器端进行连接，一旦连接成功，客户端和服务器端就可以进行通信了"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96f76c50",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/18.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfcb34ea",
   "metadata": {},
   "source": [
    "在Python 中，通常用一个Socket表示“打开了一个网络连接”，语法格式如下：\n",
    "\n",
    "**socket.socket([family[, type[, proto]]])**\n",
    "- family : 套接字家族可以使 AF_UNIX 或者 AF_INET \n",
    "    - AF 表示ADDRESS FAMILY 地址族\n",
    "    - AF_INET（又称 PF_INET）是 IPv4 网络协议的套接字类型；\n",
    "    - AF_UNIX 则是 Unix 系统本地通信。\n",
    "    \n",
    "    \n",
    "- type : 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM 或 SOCK_DGRAM \n",
    "    - Socket主要分为 面向连接的Socket 和 无连接的Socket\n",
    "    - 无连接Socket的主要协议是用户数据报协议，也就是常说的UDP，\n",
    "    - UDP Socket的名字是 SOCK_DGRAM \n",
    "    - TCP Socket的名字是 SOCK_STREAM\n",
    "\n",
    "\n",
    "- protocol : 一般不填，默认为0。\n",
    "\n",
    "**示例代码如下：**\n",
    "- udpSocket=socket.socket (AF_INET,SOCK_DGRAM)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ca80ba9",
   "metadata": {},
   "source": [
    "### socket对象的内置函数和属性"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8aa23bf",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/19.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5379d55",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/20.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7f2c1cc",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/21.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f36891ca",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/22.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ffbf798",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/23.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68ff49c8",
   "metadata": {},
   "source": [
    "## UDP编程\n",
    "UDP协议时，不需要建立连接，只需要知道对方的IP地址和端口号，就可以直接发数据包。但是，能不能到达就不知道了。\n",
    "\n",
    "虽然用UDP传输数据不可靠，但它的优点是和TCP比，速度快，对于不要求可靠到达的数据，就可以使用UDP协议。\n",
    "\n",
    "创建Socket时， SOCK_DGRAM 指定了这个Socket的类型是UDP。\n",
    "\n",
    "绑定端口和TCP一样，但是不需要调用 listen() 方法，而是直接接收来自任何客户端的数据。 \n",
    "\n",
    "recvfrom() 方法返回数据和客户端的地址与端口，这样，服务器收到数据后，直接调用 sendto() 就可以把数据用UDP发给客户端。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "450215cf",
   "metadata": {},
   "source": [
    "### UDP接收一条数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1556ba56",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-10-08T07:41:58.211Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "等待接收数据！\n"
     ]
    }
   ],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "from socket import *\n",
    "\n",
    "# 创建 UDP类型的套接字\n",
    "s = socket(AF_INET, SOCK_DGRAM)  \n",
    "\n",
    "s.bind((\"127.0.0.1\", # 绑定的IP地址\n",
    "        8888)) # 接收信息的端口\n",
    "\n",
    "print(\"等待接收数据！\")\n",
    "\n",
    "# 返回数据、客户端的地址和端口\n",
    "recv_data = s.recvfrom(1024)  # 1024表示本次接收的最大字节数\n",
    "\n",
    "print(f\"收到远程信息:{recv_data[0].decode('gbk')},from {recv_data[1]}\")\n",
    "\n",
    "# 关闭\n",
    "s.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7eb19ca",
   "metadata": {},
   "source": [
    "### UDP发送一条数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a50f593e",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-10-08T07:42:00.416Z"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "from socket import *\n",
    "\n",
    "# 创建UDP类型的套接字\n",
    "s = socket(AF_INET, SOCK_DGRAM)\n",
    "\n",
    "\n",
    "addr = (\"127.0.0.1\", # 要发送到的IP地址\n",
    "        8888) # 要发送到的端口\n",
    "\n",
    "data = input(\"请输入：\")\n",
    "\n",
    "s.sendto(data.encode(\"gbk\"), addr)\n",
    "\n",
    "# 关闭\n",
    "s.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6f73a30",
   "metadata": {},
   "source": [
    "### UDP持续接收数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "deeda147",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "\n",
    "# 创建UDP类型的套接字\n",
    "s = socket(AF_INET, SOCK_DGRAM)  \n",
    "\n",
    "s.bind((\"127.0.0.1\", 8888)) # 接收信息的端口\n",
    "\n",
    "print(\"等待接收数据！\")\n",
    "\n",
    "while True:\n",
    "    \n",
    "    # 1024表示本次接收的最大字节数 -- 如果没有接收到信息，则阻塞\n",
    "    recv_data = s.recvfrom(1024)  \n",
    "    \n",
    "    # 解码，获取信息\n",
    "    recv_content = recv_data[0].decode('gbk')\n",
    "    \n",
    "    print(f\"收到远程信息:{recv_content},from {recv_data[1]}\")\n",
    "    \n",
    "    # 如果获取到信息是 88，则不再接收数据\n",
    "    if recv_content == \"88\":\n",
    "        print(\"结束聊天！\")\n",
    "        break\n",
    "\n",
    "# 关闭\n",
    "s.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b6efca5",
   "metadata": {},
   "source": [
    "### UDP持续发送数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c04f5a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "\n",
    "from socket import *\n",
    "\n",
    "# 创建UDP类型的套接字\n",
    "s = socket(AF_INET, SOCK_DGRAM)  \n",
    "\n",
    "# 绑定IP地址 和 要发送到的端口\n",
    "addr = (\"127.0.0.1\", 8888)\n",
    "\n",
    "while True:\n",
    "    data = input(\"请输入：\")\n",
    "    \n",
    "    # 发送信息\n",
    "    s.sendto(data.encode(\"gbk\"), addr)\n",
    "    \n",
    "    # 如果发送的信息是 88，则不再发送信息\n",
    "    if data == \"88\":\n",
    "        print(\"结束聊天！\")\n",
    "        break\n",
    "\n",
    "# 关闭\n",
    "s.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a2069ea",
   "metadata": {},
   "source": [
    "### UDP多线程实现双向通信\n",
    "**此时不严格区分 客户端 和 服务端，为了方便实验，不妨加以区分**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7444e74",
   "metadata": {},
   "source": [
    "#### 服务端"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "814a8092",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "from socket import *\n",
    "from threading import Thread\n",
    "\n",
    "def recv_data():\n",
    "    \"\"\"接收数据\"\"\"\n",
    "    while True:\n",
    "        \n",
    "        # 接收数据 -- 返回信息、(客户端IP、客户端的端口号)\n",
    "        recv_data = s.recvfrom(1024)  # 1024表示本次接收的最大字节数\n",
    "        \n",
    "        # 获取信息\n",
    "        recv_content = recv_data[0].decode('gbk')\n",
    "        \n",
    "        print(f\"收到远程信息:{recv_content},from {recv_data[1]}\")\n",
    "        \n",
    "        # 如果接收到的是 88，则停止接收信息\n",
    "        if recv_content == \"88\":\n",
    "            print(\"结束聊天！\")\n",
    "            break\n",
    "            \n",
    "            \n",
    "def send_data():\n",
    "    \"\"\"发送数据\"\"\"\n",
    "    addr = (\"127.0.0.1\", # 绑定对方的IP\n",
    "            9999) # 要发送到的端口\n",
    "    \n",
    "    while True:\n",
    "        data = input(\"请输入：\\n\")\n",
    "        \n",
    "        # 发送数据\n",
    "        s.sendto(data.encode(\"gbk\"), addr)\n",
    "        \n",
    "        # 如果发送的信息是 88，则不再发送\n",
    "        if data == \"88\":\n",
    "            print(\"结束聊天！\")\n",
    "            break\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # 创建UDP类型的套接字\n",
    "    s = socket(AF_INET, SOCK_DGRAM)  \n",
    "    \n",
    "    # 绑定接收信息的端口, ip可以不写（就是本机的IP）\n",
    "    s.bind((\"127.0.0.1\", 8888)) \n",
    "    \n",
    "    # 创建两个线程 -- 一个负责接收信息，一个负责发送信息\n",
    "    t1 = Thread(target=recv_data)\n",
    "    t2 = Thread(target=send_data)\n",
    "    \n",
    "    # 启动线程\n",
    "    t1.start()\n",
    "    t2.start()\n",
    "    \n",
    "    # 子线程不结束，主线程也不结束\n",
    "    t1.join()\n",
    "    t2.join()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0acb5fc",
   "metadata": {},
   "source": [
    "#### 客户端"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4827a4bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "from socket import *\n",
    "from threading import Thread\n",
    "\n",
    "def recv_data():\n",
    "    \"\"\"接收数据\"\"\"\n",
    "    while True:\n",
    "        \n",
    "        # 接收数据 -- 返回信息、(客户端IP、客户端的端口号)\n",
    "        recv_data = s.recvfrom(1024)  # 1024表示本次接收的最大字节数\n",
    "        \n",
    "        # 获取信息\n",
    "        recv_content = recv_data[0].decode('gbk')\n",
    "        \n",
    "        print(f\"收到远程信息:{recv_content},from {recv_data[1]}\")\n",
    "        \n",
    "        # 如果接收到的是 88，则停止接收信息\n",
    "        if recv_content == \"88\":\n",
    "            print(\"结束聊天！\")\n",
    "            break\n",
    "            \n",
    "            \n",
    "def send_data():\n",
    "    \"\"\"发送数据\"\"\"\n",
    "    addr = (\"127.0.0.1\", # 绑定对方的IP\n",
    "            8888) # 要发送到的端口\n",
    "    \n",
    "    while True:\n",
    "        data = input(\"请输入：\\n\")\n",
    "        \n",
    "        # 发送数据\n",
    "        s.sendto(data.encode(\"gbk\"), addr)\n",
    "        \n",
    "        # 如果发送的信息是 88，则不再发送\n",
    "        if data == \"88\":\n",
    "            print(\"结束聊天！\")\n",
    "            break\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # 创建UDP类型的套接字\n",
    "    s = socket(AF_INET, SOCK_DGRAM)  \n",
    "    \n",
    "    # 绑定接收信息的端口, ip可以不写（就是本机的IP）\n",
    "    s.bind((\"127.0.0.1\", 9999)) \n",
    "    \n",
    "    # 创建两个线程 -- 一个负责接收信息，一个负责发送信息\n",
    "    t1 = Thread(target=recv_data)\n",
    "    t2 = Thread(target=send_data)\n",
    "    \n",
    "    # 启动线程\n",
    "    t1.start()\n",
    "    t2.start()\n",
    "    \n",
    "    # 子线程不结束，主线程也不结束\n",
    "    t1.join()\n",
    "    t2.join()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "921679d6",
   "metadata": {},
   "source": [
    "## TCP编程\n",
    "面向连接的Socket 使用的协议是TCP协议。TCP的Socket名称是 SOCK_STREAM \n",
    "\n",
    "创建TCP套接字，可以调用 socket.socket() 。示例代码如下：\n",
    "\n",
    "tcpSocket=socket.socket(AF_INET,SOCK_STREAM)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "334377d9",
   "metadata": {},
   "source": [
    "![](02_网络编程_images/24.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26ebf26a",
   "metadata": {},
   "source": [
    "### 创建Socket服务器的步骤\n",
    "1. 创建Socket对象。\n",
    "1. 绑定端口号。\n",
    "1. 监听端口号。\n",
    "1. 等待客户端Socket的连接。\n",
    "1. 读取客户端发送过来的数据。\n",
    "1. 向客户端发送数据。\n",
    "1. 关闭客户端Socket连接。\n",
    "1. 关闭服务端Socket连接。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fd885d5",
   "metadata": {},
   "source": [
    "### TCP接收一条数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bdaa6a44",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "\n",
    "# 建立TCP套接字\n",
    "server_socket = socket(AF_INET, SOCK_STREAM)  \n",
    "\n",
    "server_socket.bind((\"127.0.0.1\", # IP地址可不写\n",
    "                    8899))  # 接收信息的端口号 -- 8899端口\n",
    "\n",
    "# 配置监听信息 -- 最多建立 5个连接\n",
    "server_socket.listen(5)\n",
    "\n",
    "print(\"等待接收连接！\")\n",
    "\n",
    "# 程序阻塞，并等待接收 连接请求\n",
    "client_socket, client_info = server_socket.accept()\n",
    "\n",
    "# 程序阻塞，等待接收信息\n",
    "recv_data = client_socket.recv(1024)  # 最大接收1024字节\n",
    "\n",
    "print(f\"收到信息:{recv_data.decode('gbk')}, 来自:{client_info}\")\n",
    "\n",
    "# 关闭\n",
    "client_socket.close()\n",
    "server_socket.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "317982b5",
   "metadata": {},
   "source": [
    "### TCP发送一条数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7b5be6c",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "\n",
    "client_socket = socket(AF_INET, SOCK_STREAM)\n",
    "\n",
    "# 建立连接，传入 发送到的IP地址、发送到的端口号\n",
    "client_socket.connect((\"127.0.0.1\", 8899))\n",
    "\n",
    "# 发送信息\n",
    "data = input('请输入：\\n')\n",
    "client_socket.send(data.encode(\"gbk\"))\n",
    "\n",
    "# 关闭\n",
    "client_socket.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04f9e998",
   "metadata": {},
   "source": [
    "### TCP互发信息-服务端"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34eaea05",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "\n",
    "# 建立 TCP套接字\n",
    "server_socket = socket(AF_INET, SOCK_STREAM)  \n",
    "\n",
    "server_socket.bind((\"127.0.0.1\", # 本机的IP，可不填\n",
    "                    8899))  # 接收信息的端口号\n",
    "\n",
    "# 配置监听信息 -- 最多建立 5个连接\n",
    "server_socket.listen(5)\n",
    "\n",
    "# 程序阻塞，等待客户端建立连接\n",
    "print(\"等待接收连接！\")\n",
    "client_socket, client_info = server_socket.accept() # 返回客户端的 socket\n",
    "\n",
    "print(\"一个客户端建立连接成功！\")\n",
    "\n",
    "\n",
    "while True:\n",
    "    \n",
    "    # 程序阻塞，等待接收信息\n",
    "    recv_data = client_socket.recv(1024)  # 最大接收1024字节\n",
    "    \n",
    "    # 解码，读取信息\n",
    "    recv_content = recv_data.decode('gbk')\n",
    "    print(f\"客户端说:{recv_content}, 来自:{client_info}\")\n",
    "    \n",
    "    # 如果接收的信息是 end,则不再接收信息\n",
    "    if recv_content == \"end\":\n",
    "        break\n",
    "    \n",
    "    # 发信息给客户端\n",
    "    msg = input(\">\")\n",
    "    client_socket.send(msg.encode(\"gbk\"))\n",
    "\n",
    "# 关闭\n",
    "client_socket.close()\n",
    "server_socket.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f644fb34",
   "metadata": {},
   "source": [
    "### TCP互发信息-客户端"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60a4fc03",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "\n",
    "# 创建 TCP套接字\n",
    "client_socket = socket(AF_INET, SOCK_STREAM)\n",
    "\n",
    "# 建立连接\n",
    "client_socket.connect((\"127.0.0.1\", # 要发送到的IP地址\n",
    "                       8899)) # 信息要发送到的 端口号\n",
    "\n",
    "while True:\n",
    "    # 给服务端发消息\n",
    "    msg = input(\">\")\n",
    "    client_socket.send(msg.encode(\"gbk\"))\n",
    "    \n",
    "    # 如果发送的是 end, 则不再发送\n",
    "    if msg == \"end\":\n",
    "        break\n",
    "        \n",
    "    # 接收服务器端数据\n",
    "    recv_data = client_socket.recv(1024)  # 最大接收1024字节\n",
    "    print(f\"服务器端说:{recv_data.decode('gbk')}\")\n",
    "\n",
    "# 关闭\n",
    "client_socket.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77977ccc",
   "metadata": {},
   "source": [
    "### TCP服务端-运用多线程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "317077fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"服务端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "from threading import Thread\n",
    "\n",
    "\n",
    "def recv_data():\n",
    "    \"\"\"接收信息\"\"\"\n",
    "    \n",
    "    while True:\n",
    "        \n",
    "        # 程序阻塞，等待接收信息\n",
    "        recv_data = client_socket.recv(1024)  # 最大接收1024字节\n",
    "        \n",
    "        # 解码，获取信息\n",
    "        recv_content = recv_data.decode(\"gbk\")\n",
    "        print(f\"客户端说:{recv_content},来自:{client_info}\")\n",
    "        \n",
    "        # 如果接收到的信息为 end，则不再接收信息\n",
    "        if recv_content == \"end\":\n",
    "            print(\"结束接收消息！\")\n",
    "            break\n",
    "\n",
    "\n",
    "def send_data():\n",
    "    \"\"\"发送信息\"\"\"\n",
    "    \n",
    "    while True:\n",
    "        msg = input(\"请输入：\\n\")\n",
    "        \n",
    "        # 发送信息\n",
    "        client_socket.send(msg.encode(\"gbk\"))\n",
    "        \n",
    "        # 如果发送的信息是 end, 则不再发送信息\n",
    "        if msg == \"end\":\n",
    "            print(\"结束发送消息！\")\n",
    "            break\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    \n",
    "    # 建立 TCP套接字\n",
    "    server_socket = socket(AF_INET, SOCK_STREAM)  \n",
    "    \n",
    "    server_socket.bind((\"127.0.0.1\", # 本机IP，可不填\n",
    "                        8899))  # 接收信息的端口号\n",
    "    \n",
    "    # 配置监听信息 -- 最多建立 5个连接\n",
    "    server_socket.listen(5)\n",
    "    \n",
    "    # 程序阻塞，等待建立连接\n",
    "    print(\"等待接收连接！\")\n",
    "    client_socket, client_info = server_socket.accept() # 获取 客户端的socket\n",
    "    print(\"一个客户端建立连接成功！\")\n",
    "\n",
    "    # 创建两个线程 -- 一个负责发送信息，一个负责接收信息\n",
    "    t1 = Thread(target=recv_data)\n",
    "    t2 = Thread(target=send_data)\n",
    "    \n",
    "    # 启动线程\n",
    "    t1.start()\n",
    "    t2.start()\n",
    "\n",
    "    # 等待子线程结束，主线程才结束\n",
    "    t1.join()\n",
    "    t2.join()\n",
    "\n",
    "    # 关闭\n",
    "    client_socket.close()\n",
    "    server_socket.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28c30179",
   "metadata": {},
   "source": [
    "### TCP客户端-运用多线程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19318178",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"客户端\"\"\"\n",
    "\n",
    "\"\"\"jupyter notebook中，不利于实验网络编程代码，请将下面代码复制到 Pycharm 中进行展示：\"\"\"\n",
    "\n",
    "from socket import *\n",
    "from threading import Thread\n",
    "\n",
    "def recv_data():\n",
    "    \"\"\"接收信息\"\"\"\n",
    "    \n",
    "    while True:\n",
    "        # 程序挂起，等待接收数据\n",
    "        recv_data = client_socket.recv(1024)  # 最大接收1024字节\n",
    "        \n",
    "        # 解码，获取信息\n",
    "        recv_content = recv_data.decode('gbk')\n",
    "        print(f\"服务器端说: {recv_content}\")\n",
    "        \n",
    "        # 如果接收的信息是 end，则不再接收信息\n",
    "        if recv_content == \"end\":\n",
    "            print(\"结束接收消息\")\n",
    "            break\n",
    "\n",
    "def send_data():\n",
    "    \"\"\"发送信息\"\"\"\n",
    "    \n",
    "    while True:\n",
    "        # 给服务端发消息\n",
    "        msg = input(\"请输入：\\n\")\n",
    "        client_socket.send(msg.encode(\"gbk\"))\n",
    "        \n",
    "        # 如果发送的信息是 end，则不再发送信息\n",
    "        if msg == \"end\":\n",
    "            break\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    \n",
    "    # 创建 TCP套接字\n",
    "    client_socket = socket(AF_INET, SOCK_STREAM)\n",
    "    \n",
    "    # 建立连接\n",
    "    client_socket.connect((\"127.0.0.1\", # 连接到的 IP地址\n",
    "                           8899)) # 信息发送到的 端口号\n",
    "\n",
    "    # 创建 两个线程 -- 一个负责发送信息，一个负责接收信息\n",
    "    t1 = Thread(target=recv_data)\n",
    "    t2 = Thread(target=send_data)\n",
    "    \n",
    "    # 启动线程\n",
    "    t1.start()\n",
    "    t2.start()\n",
    "    \n",
    "    # 等待子线程结束，主线程才结束\n",
    "    t1.join()\n",
    "    t2.join()\n",
    "\n",
    "    # 关闭\n",
    "    client_socket.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2f9f4bf",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "974b46a9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "288px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
